use super::Result;
use super::{dept::DeptListVo, role::RoleSimpletVo};
use crate::{
    entity::{
        sys_dept, sys_resource, sys_role, sys_role_resource,
        sys_user::{self, UserEnabledDept},
        sys_user_role,
    },
    password,
    util::current_datetime,
};
use chrono::NaiveDateTime;
use sea_orm::{
    sea_query::{Expr, IntoCondition},
    ActiveModelTrait,
    ActiveValue::NotSet,
    ColumnTrait, Condition, ConnectionTrait, DbConn, DerivePartialModel, EntityTrait,
    FromQueryResult, JoinType, ModelTrait, Order, PaginatorTrait, PartialModelTrait, QueryFilter,
    QueryOrder, QuerySelect, QueryTrait, RelationTrait, Set, TransactionTrait,
};
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::entity::sys_user::Entity as SystemUser;

use crate::entity::sys_user::Model as UserModel;

pub struct User;
pub type UserId = i32;

#[derive(Debug, DerivePartialModel, FromQueryResult)]
#[sea_orm(entity = "SystemUser")]
pub struct UserAvatar {
    pub user_id: i32,
    pub avatar: String,
}

/// 用户的展示数据结构
#[derive(Debug, Clone, DerivePartialModel, FromQueryResult, Serialize, Deserialize)]
#[sea_orm(entity = "SystemUser")]
pub struct UserVo {
    pub user_id: UserId,
    pub dept_id: i32,
    pub username: String,
    pub nickname: String,
    pub status: bool,
    // pub created_at: NaiveDateTime,
    // pub updated_at: NaiveDateTime,
}

/// 用户的详情数据, 包含全部可用角色
#[derive(Debug, Serialize)]
pub struct UserDetailVo {
    pub user_id: UserId,
    pub dept_id: i32,
    pub avatar: String,
    pub username: String,
    pub nickname: String,
    pub phone: String,
    pub email: String,
    pub sex: i8,
    pub remark: String,
    pub status: bool,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
    pub roles: Vec<RoleSimpletVo>,
    pub dept: Option<DeptListVo>,
}

/// 用户简略的数据，用于 list 的展示
#[derive(Debug, FromQueryResult, Serialize)]
pub struct UserSimpletVo {
    pub user_id: UserId,
    pub dept_id: i32,
    pub dept_name: Option<String>,
    pub username: String,
    pub nickname: String,
    pub status: bool,
    pub created_at: NaiveDateTime,
}

/// 用户列表展示的filter选项
#[derive(Debug, Deserialize)]
pub struct UserListFilter {
    pub username: Option<String>,
    pub nickname: Option<String>,
    pub status: Option<String>,
}

/// 创建用户的数据
#[derive(Deserialize, Validate)]
pub struct NewUserDo {
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub username: String,
    #[validate(length(min = 3, max = 32))]
    pub nickname: String,
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub password: String,
    pub dept_id: i32,
    pub status: bool,
    pub roles: Option<Vec<i32>>,
    #[validate(custom(function = "crate::util::validate_phone"))]
    #[serde(default)]
    pub phone: String,
    #[validate(custom(function = "crate::util::validate_email"))]
    #[serde(default)]
    pub email: String,
    #[serde(default)]
    pub sex: i8,
    #[validate(length(max = 255))]
    #[serde(default)]
    pub remark: String,
}

/// 修改用户的数据
#[derive(Deserialize, Validate)]
pub struct UpdateUserDo {
    pub avatar: Option<String>,
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub nickname: Option<String>,
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub password: Option<String>,
    #[validate(custom(function = "crate::util::validate_phone"))]
    pub phone: Option<String>,
    #[validate(custom(function = "crate::util::validate_email"))]
    pub email: Option<String>,
    pub sex: Option<i8>,
    #[validate(length(max = 255))]
    pub remark: Option<String>,
    pub dept_id: Option<i32>,
    pub status: Option<bool>,
    pub roles: Option<Vec<i32>>,
}

#[derive(Deserialize, Validate)]
pub struct ChangePasswordDo {
    #[validate(length(min = 3, max = 32), non_control_character)]
    old_pwd: String,
    #[validate(length(min = 3, max = 32), non_control_character)]
    new_pwd: String,
}

impl User {
    pub async fn get_by_name_password(
        conn: &DbConn,
        name: &str,
        password: &str,
    ) -> Result<Option<(UserVo, bool)>> {
        SystemUser::find()
            .filter(sys_user::Column::DelFlag.eq(false))
            .filter(sys_user::Column::Username.eq(name))
            .one(conn)
            .await
            .map(|user| {
                user.map(|user| {
                    (
                        UserVo {
                            user_id: user.user_id,
                            dept_id: user.dept_id,
                            username: user.username,
                            nickname: user.nickname,
                            status: user.status,
                        },
                        crate::password::verify(password, &user.password).unwrap_or(false),
                    )
                })
            })
    }

    /// 根据usernaem获得用户, 软删除的用户返回None
    #[inline]
    pub async fn get_by_name<T>(conn: &DbConn, name: &str, state: Option<bool>) -> Result<Option<T>>
    where
        T: PartialModelTrait,
    {
        sys_user::Entity::find()
            .filter(sys_user::Column::DelFlag.eq(false))
            .filter(sys_user::Column::Username.eq(name))
            .apply_if(state, |sql, state| {
                sql.filter(sys_user::Column::Status.eq(state))
            })
            .into_partial_model()
            .one(conn)
            .await
    }

    /// 根据id获得用户, 软删除的用户返回None
    #[inline]
    pub async fn get_by_id<T>(conn: &DbConn, id: UserId, state: Option<bool>) -> Result<Option<T>>
    where
        T: PartialModelTrait,
    {
        sys_user::Entity::find_by_id(id)
            .filter(sys_user::Column::DelFlag.eq(false))
            .apply_if(state, |sql, state| {
                sql.filter(sys_user::Column::Status.eq(state))
            })
            .into_partial_model()
            .one(conn)
            .await
    }

    /// 获取一个用户的详细信息, 包含角色和单位
    /// - 不检查用户是否可用 status
    /// - 检查用户是否软删除 del_flag
    pub async fn get_detail_by_id(db: &DbConn, id: UserId) -> Result<Option<UserDetailVo>> {
        let user = if let Some(res) = sys_user::Entity::find_by_id(id)
            .filter(sys_user::Column::DelFlag.eq(0))
            .all(db)
            .await?
            .into_iter()
            .next()
        {
            res
        } else {
            return Ok(None);
        };
        let roles = user
            .find_linked(sys_user_role::UserEnabledRoles)
            .all(db)
            .await?;
        let dept = user
            .find_linked(UserEnabledDept)
            .into_model::<DeptListVo>()
            .one(db)
            .await?;
        Ok(Some(UserDetailVo {
            user_id: user.user_id,
            dept_id: user.dept_id,
            avatar: user.avatar,
            dept,
            username: user.username,
            nickname: user.nickname,
            status: user.status,
            created_at: user.created_at,
            updated_at: user.updated_at,
            roles: roles.into_iter().map(Into::into).collect(),
            phone: user.phone,
            email: user.email,
            sex: user.sex,
            remark: user.remark,
        }))
    }

    /// 列出所有用户
    ///
    /// # Params
    /// - page page_size 分页信息
    /// - dept  是否从单位id中筛序, 包括本单位及以下部门
    /// - order 是否启用排序,根据创建时间排序
    /// - filter 过滤
    pub async fn list(
        db: &DbConn,
        page: u64,
        page_size: u64,
        dept: Option<i32>,
        order: Option<bool>,
        filter: Option<UserListFilter>,
    ) -> Result<(u64, Vec<UserSimpletVo>)> {
        let mut sql = sys_user::Entity::find()
            .select_only()
            .column(sys_user::Column::UserId)
            .filter(sys_user::Column::DelFlag.eq(0))
            .left_join(sys_dept::Entity)
            .column(sys_user::Column::DeptId)
            .column(sys_user::Column::Username)
            .column(sys_user::Column::Nickname)
            .column(sys_user::Column::Status)
            .column(sys_user::Column::CreatedAt)
            .column(sys_dept::Column::DeptName)
            .apply_if(filter, |sql, filter| {
                sql.apply_if(filter.nickname, |sql, nickname| {
                    sql.filter(sys_user::Column::Nickname.contains(nickname))
                })
                .apply_if(filter.username, |sql, username| {
                    sql.filter(sys_user::Column::Username.contains(username))
                })
                .apply_if(filter.status, |sql, status| match status.as_str() {
                    "true" => sql.filter(sys_user::Column::Status.eq(true)),
                    "false" => sql.filter(sys_user::Column::Status.eq(false)),
                    _ => sql,
                })
            })
            .apply_if(order, |sql, asc| {
                sql.order_by(
                    sys_user::Column::CreatedAt,
                    if asc { Order::Asc } else { Order::Desc },
                )
            });
        if let Some(dept) = dept {
            // 查找单位是否存在且可用
            let dept = if let Some(dept) = sys_dept::Entity::find_by_id(dept)
                .filter(sys_dept::Column::DelFlag.eq(0))
                .one(db)
                .await?
            {
                dept
            } else {
                return Ok((0, vec![]));
            };
            let child_ancestors = format!("{}{}/", dept.ancestors, dept.dept_id);
            sql = sql.filter(
                Condition::any()
                    .add(sys_user::Column::DeptId.eq(dept.dept_id))
                    .add(
                        sys_user::Column::DeptId.in_subquery(
                            sys_dept::Entity::find()
                                .select_only()
                                .column(sys_dept::Column::DeptId)
                                .filter(sys_dept::Column::Status.eq(true))
                                .filter(sys_dept::Column::DelFlag.eq(0))
                                .filter(sys_dept::Column::Ancestors.starts_with(child_ancestors))
                                .into_query(),
                        ),
                    ),
            );
        }
        let sql = sql.into_model::<UserSimpletVo>().paginate(db, page_size);
        let total = sql.num_items().await?;
        let users = sql.fetch_page(page).await?;
        Ok((total, users))
    }

    /// 创建一个用户
    /// - 不检查dept和role关系的有效性
    /// - 不检查username的重名
    pub async fn create(db: &DbConn, data: NewUserDo) -> Result<UserModel> {
        let active = sys_user::ActiveModel {
            user_id: NotSet,
            dept_id: Set(data.dept_id),
            username: Set(data.username),
            nickname: Set(data.nickname),
            avatar: Set("".to_owned()),
            status: Set(data.status),
            del_flag: Set(false),
            password: Set(password::hash(&data.password).unwrap()),
            phone: Set(data.phone),
            email: Set(data.email),
            sex: Set(data.sex),
            remark: Set(data.remark),
            created_at: Set(current_datetime()),
            updated_at: Set(current_datetime()),
        };
        let txn = db.begin().await?;
        let user = active.insert(&txn).await?;
        if let Some(roles) = data.roles {
            Self::update_roles(&txn, user.user_id, roles).await?;
        }
        txn.commit().await?;
        Ok(user)
    }

    /// 修改一个用户
    /// - 不检查dept和role关系的有效性
    /// - 不检查用户是否存在
    pub async fn update(db: &DbConn, user_id: UserId, data: UpdateUserDo) -> Result<UserModel> {
        let active = sys_user::ActiveModel {
            user_id: Set(user_id),
            dept_id: data.dept_id.map(Set).unwrap_or(NotSet),
            avatar: data.avatar.map(Set).unwrap_or(NotSet),
            nickname: data.nickname.map(Set).unwrap_or(NotSet),
            status: data.status.map(Set).unwrap_or(NotSet),
            phone: data.phone.map(Set).unwrap_or(NotSet),
            email: data.email.map(Set).unwrap_or(NotSet),
            sex: data.sex.map(Set).unwrap_or(NotSet),
            remark: data.remark.map(Set).unwrap_or(NotSet),
            password: data
                .password
                .map(|s| password::hash(&s).unwrap())
                .map(Set)
                .unwrap_or(NotSet),
            updated_at: Set(current_datetime()),
            ..Default::default()
        };
        let txn = db.begin().await?;
        if let Some(roles) = data.roles {
            Self::update_roles(&txn, user_id, roles).await?;
        }
        let res = active.update(&txn).await?;
        txn.commit().await?;
        Ok(res)
    }

    /// 直接修改用户密码
    pub async fn change_password(db: &DbConn, user_id: UserId, new_psd: &str) -> Result<bool> {
        let active = sys_user::ActiveModel {
            user_id: Set(user_id),
            password: Set(password::hash(new_psd).unwrap()),
            ..Default::default()
        };

        sys_user::Entity::update(active)
            .exec(db)
            .await
            .map(|_| true)
    }

    /// 修改用户密码
    ///
    /// 当用户不存在, 未启用，软删除，原密码不正确，返回 false
    pub async fn change_password2(
        db: &DbConn,
        user_id: UserId,
        data: ChangePasswordDo,
    ) -> Result<bool> {
        let self_password = if let Some(pwd) = sys_user::Entity::find_by_id(user_id)
            .select_only()
            .column(sys_user::Column::Password)
            .filter(sys_user::Column::Status.eq(true))
            .filter(sys_user::Column::DelFlag.eq(false))
            .into_tuple::<String>()
            .one(db)
            .await?
        {
            pwd
        } else {
            return Ok(false);
        };
        if !Self::check_password(&self_password, &data.old_pwd) {
            return Ok(false);
        }
        let active = sys_user::ActiveModel {
            user_id: Set(user_id),
            password: Set(password::hash(&data.new_pwd).unwrap()),
            ..Default::default()
        };
        sys_user::Entity::update(active)
            .exec(db)
            .await
            .map(|_| true)
    }

    /// 软删除用户, 将del_flag字段设置为[true]
    ///
    /// 用户不存在抛出user_id不存在DbErr
    pub async fn delete(db: &DbConn, user_id: &UserId) -> Result<bool> {
        let active = sys_user::ActiveModel {
            user_id: Set(user_id.clone()),
            del_flag: Set(true),
            updated_at: Set(current_datetime()),
            ..Default::default()
        };
        sys_user::Entity::update(active)
            .exec(db)
            .await
            .map(|_| true)
    }

    /// 检查用户名是否存在
    /// - username 检查长度不在3～32，直接返回false
    pub async fn check_name(db: &DbConn, username: &str) -> Result<bool> {
        if username.len() < 3 || username.len() > 32 {
            return Ok(false);
        }
        SystemUser::find()
            .filter(sys_user::Column::Username.eq(username))
            .count(db)
            .await
            .map(|f| f > 0)
    }

    /// 查询一个用户的全部**可用**角色
    pub async fn roles(conn: &DbConn, user_id: UserId) -> Result<Vec<RoleSimpletVo>> {
        sys_role::Entity::find()
            .join(
                JoinType::LeftJoin,
                sys_user_role::Relation::SysRole
                    .def()
                    .on_condition(move |_l, r| {
                        Expr::col((r, sys_user_role::Column::UserId))
                            .eq(user_id)
                            .into_condition()
                    }),
            )
            .filter(sys_role::Column::Status.eq(0))
            .filter(sys_role::Column::DelFlag.eq(0))
            .into_partial_model::<RoleSimpletVo>()
            .all(conn)
            .await
    }

    /// 更新 sys_user_role 表
    ///
    /// 不检查 user_id 和 roles id 是否存在
    pub async fn update_roles<C>(txn: &C, user_id: UserId, roles: Vec<i32>) -> Result<()>
    where
        C: ConnectionTrait,
    {
        let roles = roles
            .into_iter()
            .map(|r_id| sys_user_role::ActiveModel {
                role_id: Set(r_id),
                user_id: Set(user_id.clone()),
            })
            .collect::<Vec<_>>();
        sys_user_role::Entity::delete_many()
            .filter(sys_user_role::Column::UserId.eq(user_id))
            .exec(txn)
            .await?;
        if roles.len() > 0 {
            sys_user_role::Entity::insert_many(roles).exec(txn).await?;
        }
        Ok(())
    }

    /// 检查密码是否正确
    /// - hashed_pwd 数据库保存的hash密码
    /// - pwd 要验证的密码原文
    #[inline]
    pub fn check_password(hashed_pwd: &str, pwd: &str) -> bool {
        password::verify(pwd, &hashed_pwd).unwrap_or(false)
    }

    /// 获得用户的全部可用 resource
    pub async fn fetch_resource(db: &sea_orm::DbConn, id: i32) -> Result<Vec<(String, String)>> {
        sys_user::Entity::find()
            .select_only()
            .column(sys_resource::Column::Path)
            .column(sys_resource::Column::Method)
            .join(JoinType::InnerJoin, sys_user::Relation::SysUserRole.def())
            .join(JoinType::InnerJoin, sys_user_role::Relation::SysRole.def())
            .join(
                sea_orm::JoinType::InnerJoin,
                sys_role::Relation::SysRoleResource.def(),
            )
            .join(
                sea_orm::JoinType::InnerJoin,
                sys_role_resource::Relation::SysResource.def(),
            )
            .filter(sys_resource::Column::Status.eq(true))
            .filter(sys_role::Column::Status.eq(true))
            .filter(sys_user::Column::UserId.eq(id))
            .into_tuple::<(String, String)>()
            .all(db)
            .await
    }
}
